import mongoose, { AnyObject, Expression, FilterQuery, Model, ObjectId, PipelineStage, Types, UpdateQuery } from "mongoose";
import { name } from "../../package.json";
import { PageReq } from "@/types/common/common";
import { Response } from "@/middleware/response";
export const connectMongoDB = () => {
    new Promise((resolve, reject) => {
        mongoose.connect(`mongodb://127.0.0.1:27017/${name}`);
        mongoose.connection.on("open", err => {
            if (err) {
                console.log(err);
                console.log("MongoDB连接失败！");
                reject(err);
                return;
            } else {
                console.log("MongoDB连接成功");
                resolve(null);
            }
        });

        mongoose.connection.on("error", err => {
            // 捕获所有Mongoose操作中的异常
            console.error({ err });
        });
    });
};

export const catchError = async (options: { operation: () => Promise<unknown>; res: Response; type?: string }): Promise<any> => {
    const { operation, res, type } = options;
    try {
        const data = await operation();
        // sendResponse方法会进行重复响应判断，只会响应一次
        res.sendResponse!(200, data || null, `${type || "操作"}成功`);
        return data;
    } catch (error) {
        if (error instanceof Error) {
            console.log(error.message);
            return res.sendResponse!(500, null, error.message);
        }
        const responseError = error as { status?: number; message?: string };
        const { status, message } = responseError;
        if (status && message) return res.sendResponse!(status, null, message);
        res.sendResponse!(500, null, `${type || "操作"}失败`);
    }
};

export interface GetPageListOptions<T> {
    model: Model<T>;
    params: PageReq;
    $project?: { [field: string]: any };
    $sort?: Record<string, 1 | Expression.Meta | -1>;
    $match?: FilterQuery<any>;
    $lookupList?: {
        localField: string;
        foreignField?: string;
        as?: string;
        from: string;
    }[];
}
// 查询分页列表
export const getPageList = async <T = any>(options: GetPageListOptions<T>) => {
    const { model, params, $sort, $project, $match, $lookupList } = options;
    let { page = 1, pageSize = 10 } = params;
    page = +page;
    pageSize = +pageSize;
    const pipeline: PipelineStage[] = [{ $skip: pageSize * (page - 1) }, { $limit: pageSize }];
    pipeline.unshift({ $sort: { ...($sort || {}), _id: -1 } });
    if ($project) pipeline.unshift({ $project });
    if ($match) pipeline.unshift({ $match });
    if ($lookupList) {
        $lookupList.forEach(item => {
            pipeline.unshift({
                $lookup: {
                    from: item.from,
                    localField: item.localField,
                    foreignField: item.foreignField || "_id", // 默认使用 _id
                    as: item.as || item.localField, // 默认使用 localField 作为别名
                },
            });
            if (item.localField.endsWith("Id")) {
                pipeline.push({
                    $unwind: `$${item.as || item.localField}`,
                });
            }
        });
    }
    const list = await model.aggregate<T>(pipeline);
    const total = await model.countDocuments($match);
    return { list, total };
};

export interface GetChildrenPageListOptions<T> {
    parentModel: Model<any>;
    model: Model<T>;
    params: PageReq;
    $project?: { [field: string]: any };
    $sort?: Record<string, 1 | Expression.Meta | -1>;
    $match?: FilterQuery<any>;
    localField: string;
    foreignField: string;
    parentKey: string;
    parentValue: any;
}
// 根据父表字段查询子表分页列表
// 例：传入Dictionary的_id，查询对应关联字段dictItems对应的DictionaryItems子表的分页列表
export const getChildrenPageList = async <T = any>(options: GetChildrenPageListOptions<T>) => {
    const { model, params, $sort, $project, $match = {}, parentModel, localField, foreignField, parentKey, parentValue } = options;
    let { page = 1, pageSize = 10 } = params;
    page = +page;
    pageSize = +pageSize;
    const pipeline: PipelineStage[] = [{ $skip: pageSize * (page - 1) }, { $limit: pageSize }];
    if ($sort) pipeline.unshift({ $sort });
    if ($project) pipeline.unshift({ $project });
    // 查询父数据，再把父数据的关联字段设置为子数据的查询条件
    const parentRes = await parentModel.findOne({ [parentKey]: parentValue });
    const foreignValues = (parentRes?.[localField] as Types.ObjectId[]) || [];
    $match[foreignField] = { $in: foreignValues };
    pipeline.unshift({ $match });
    const list = await model.aggregate(pipeline);
    const total = await model.find({ ...$match }).countDocuments();
    return { list, total };
};

export interface UpdateRecordOptions<T> {
    _id: string;
    childFields?: { key: string; value?: any | any[]; model: Model<any> }[];
    model: Model<T>;
    data: Record<string, any>;
}
// 更新数据
export const updateRecord = async <T = any>(options: UpdateRecordOptions<T>) => {
    const { _id, model, childFields, data } = options;
    if (!Types.ObjectId.isValid(_id)) throw Error("_id格式错误");
    const oldRecord = (await model.findOne({ _id })) as Record<string, any> | undefined;
    if (!oldRecord) throw Error("数据不存在");
    if (!childFields?.length) return await model.updateOne({ _id }, data as UpdateQuery<T>);
    // 声明对象存放创建的子表数据id数组，用于父表数据关联字段更新
    const updateChildrenObj: Record<string, any> = {};
    // 循环关联字段，执行创建、删除操作
    for (let index = 0; index < childFields.length; index++) {
        const { key, value, model } = childFields[index];
        if (!value) continue;
        if (Types.ObjectId.isValid(value) || (value as any[])?.every(item => Types.ObjectId.isValid(item))) {
            updateChildrenObj[key] = value;
            continue;
        }
        // 创建子表数据
        const createRes = await model.create((value as any[]).map(({ _id, ...item }) => item));
        updateChildrenObj[key] = createRes.map(({ _id }) => _id);
        const oldRecordChildFieldIds = oldRecord[key];
        // 删除子表旧数据
        await await model.deleteMany({ _id: { $in: oldRecordChildFieldIds } });
    }
    // 更新父表数据、把创建的子表数据_id更新到父表数据
    await model.updateOne({ _id }, { ...data, ...updateChildrenObj } as UpdateQuery<T>);
};

export interface CreateRecordOptions<T> {
    childFields?: { key: string; value: any | any[]; model: Model<any> }[];
    model: Model<T>;
    data: AnyObject;
}
// 创建数据
export const createRecord = async <T = any>(options: CreateRecordOptions<T>) => {
    const { model, childFields, data } = options;
    if (!childFields?.length) return await model.create(data);
    // 声明对象存放创建的子表数据id数组，用于父表数据关联字段插入
    const createChildrenObj: Record<string, any> = {};
    for (let index = 0; index < childFields.length; index++) {
        const { key, value, model } = childFields[index];
        if (!value) continue;
        if (Types.ObjectId.isValid(value) || value?.some((item: any) => Types.ObjectId.isValid(item))) {
            createChildrenObj[key] = value;
            continue;
        }
        // 创建子表数据
        const createRes = await model.create((value as any[]).map(({ _id, ...item }) => item));
        createChildrenObj[key] = createRes.map(({ _id }) => _id);
    }
    return await model.create({ ...data, ...createChildrenObj });
};

export interface DeleteRecordOptions<T = any> {
    _id: string;
    model: Model<T>;
    childFields?: { key: string; model: Model<any>; mustBeEmpty?: boolean; label?: string }[];
    parentFields?: { key: string; model: Model<any> }[];
}
// 删除数据
export const deleteRecord = async <T>(options: DeleteRecordOptions<T>) => {
    const { _id, model, childFields, parentFields = [] } = options;
    const record = (await model.findById(_id)) as Record<string, unknown | string[]>;
    if (!record) throw Error("数据不存在");
    if (childFields?.length) {
        for (let index = 0; index < childFields.length; index++) {
            const { model, key, mustBeEmpty, label } = childFields[index];
            const fieldName = label || key;
            if (mustBeEmpty && (record[key] as string[])?.length) throw Error(`${fieldName}数据大于1条，请先删除${fieldName}数据`);
            await model.deleteMany({ _id: { $in: record[key] || [] } }); // 删子表中子元素
        }
    }
    for (let index = 0; index < parentFields.length; index++) {
        const { model: parentModel, key } = parentFields[index];
        const parent = (
            await parentModel.aggregate([
                {
                    $project: { [key]: 1 },
                },
                {
                    $match: { [key]: { $elemMatch: { $eq: new Types.ObjectId(_id) } } },
                },
            ])
        )[0];
        if (!parent) continue;
        const ids = parent[key].filter((item: Types.ObjectId) => item.toString() !== _id);
        await parentModel.updateOne({ _id: parent._id }, { [key]: ids }); // 父表关联字段数组剔除当前数据_id
    }
    return await model.deleteOne({ _id });
};

export interface DeleteChildRecordOptions<T> {
    _id: string;
    model: Model<T>;
    parentModel: Model<any>;
    childField: string;
}
// 删除父数据下的子数据（两个操作：删除关联字段中的_id、删除子表数据）
export const deleteChildRecord = async <T>(options: DeleteChildRecordOptions<T>) => {
    const { _id, model, parentModel, childField } = options;
    const childRecord = await model.findOne({ _id });
    if (!childRecord) throw Error("数据不存在");
    await model.deleteOne({ _id });
    const parentRecord = await parentModel.findOne({ [childField]: _id });
    const childrenIds = (parentRecord[childField] || []).filter((item: Types.ObjectId) => item.toString() !== _id);
    await parentModel.updateOne({ _id: parentRecord._id }, { [childField]: childrenIds });
};

export interface CreateChildRecordOptions<T> {
    parentId: string;
    data: AnyObject;
    model: Model<T>;
    parentModel: Model<any>;
    childFields: string;
}
// 创建子元素（两个操作：创建子元素、把子元素_id插入到父元素关联字段数组）
export const createChildRecord = async <T>(options: CreateChildRecordOptions<T>) => {
    const { parentId, model, parentModel, childFields, data } = options;
    console.log({ parentId });
    if (!Types.ObjectId.isValid(parentId)) throw Error("_id格式错误");
    const child = await model.create(data);
    const parent = await parentModel.findOne({ _id: parentId });
    if (!parent) throw Error("父数据不存在");
    await parentModel.updateOne({ _id: parentId }, { [childFields]: [...(parent as AnyObject)[childFields], child._id] });
};

export interface GetChildrenByParentFieldOptions<T> {
    model: Model<T>;
    $match: FilterQuery<any>;
    localField: string;
    from: string;
    pipelineMatch?: FilterQuery<any>;
}
// 通过父数据字段获取子数据列表
export const getChildrenByParentField = async <T>(options: GetChildrenByParentFieldOptions<T>) => {
    const { model, $match, localField, from, pipelineMatch } = options;
    if (Object.values($match).some(item => item === undefined)) throw { status: 400, message: "参数错误" };
    if (typeof $match._id === "string") $match._id = new Types.ObjectId($match._id);
    const pipeline: Exclude<PipelineStage, PipelineStage.Merge | PipelineStage.Out>[] = [];
    if ((model.schema.obj as Record<string, object>).sort) pipeline.push({ $sort: { sort: -1 } });
    if (pipelineMatch) pipeline.push({ $match: pipelineMatch });
    const result = await model.aggregate([
        {
            $match,
        },
        {
            $lookup: {
                localField,
                foreignField: "_id",
                as: localField,
                from,
                pipeline,
            },
        },
    ]);
    if (!result.length) throw { status: 400, message: "数据不存在" };
    return result[0];
};

export interface GetRecordOptions<T> {
    _id: any;
    model: Model<T>;
    childFields?: { localField: string; from: string; foreignField?: string; as?: string }[];
}
// 通过_id查询记录以及关联查询子数据
export const getRecord = async <T>(options: GetRecordOptions<T>) => {
    const { _id, model, childFields = [] } = options;
    if (!Types.ObjectId.isValid(_id)) throw { status: 400, message: "_id格式错误" };
    const result = await model.aggregate([
        {
            $match: { _id: new Types.ObjectId(_id) },
        },
        ...childFields.map(({ localField, from, foreignField, as }) => ({
            $lookup: {
                localField,
                foreignField: foreignField || "_id",
                as: as || localField,
                from,
            },
        })),
    ]);
    return result[0];
};
